﻿// -----------------------------------------------------------------------
// <copyright file="LibDeployFiles.cs">
// Created by Robert (Bob) Williams, 2012
// </copyright>

// This file is part of DeployFileDemo.
// DeployFileGenerator is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// DeployFileGenerator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with DeployFileGenerator.  If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------


namespace Demo.DeployFile
{
    using System;
    using System.Collections;
    using System.IO;
    using System.Resources;
    using System.Security.Cryptography;
    using System.Windows.Forms;
    using System.Xml;

    /// <summary>
    /// Loads files into the Visual Studio Resources folder.
    /// </summary>
    public partial class FileInstaller
    {
        /// <summary>
        /// Deploy files from 'deployFileName' to 'stagePath'.
        /// </summary>
        /// <param name="stagePath"></param>
        /// <param name="deployFileName"></param>
        public void DeployResources(String stagePath, String deployFileName)
        {
            System.Reflection.Assembly assem = System.Reflection.Assembly.GetExecutingAssembly();
            System.IO.Stream fs = assem.GetManifestResourceStream(deployFileName);
            ResourceReader rr = new ResourceReader(fs);
            IDictionaryEnumerator dict = rr.GetEnumerator();
            XmlDocument xDoc = new XmlDocument();
            XmlNodeList xNodes;
            IDictionaryEnumerator de = rr.GetEnumerator();
            xDoc.LoadXml( DeployFileDemo.Properties.Resources.PackageFiles);
            xNodes = xDoc.SelectNodes("Files/File");
            String exePath = String.Empty;

            foreach (XmlNode xNode in xNodes)
            {
                de.Reset();

                while (de.MoveNext())
                {
                    if (xNode.Attributes["ResName"].InnerText.ToLower() == de.Key.ToString().ToLower())
                    {
                        exePath = Path.Combine(stagePath, (xNode.InnerText));
                        StageResourceFile(de.Value as Byte[], exePath, xNode.Attributes["Hash"].InnerText, xNode.Attributes["Size"].InnerText, xNode.Attributes["DateModified"].InnerText);
                    }
                }
            }
        }

        /// <summary>
        /// Calculates file length and SHA1 hash of target file and compares it to the supplied paramaters
        /// </summary>
        /// <param name="sourceSHA1"></param>
        /// <param name="sourceFileLength"></param>
        /// <param name="targetFilename"></param>
        /// <returns>true if paramaters match, else false</returns>
        public Boolean HashCheck(String sourceSHA1, UInt64 sourceFileLength, String targetFilename)
        {
            Boolean filesMatch = false;
            Boolean fileLengthsMatch = false;
            String targetSHA1;
            FileStream stream;
            SHA1 sha1 = SHA1.Create();

            stream = new FileStream(targetFilename, FileMode.Open, FileAccess.Read);
            UInt64 targetFileLength = (UInt64)stream.Length;
            targetSHA1 = BitConverter.ToString(sha1.ComputeHash(stream)).Replace("-", String.Empty);
            stream.Close();

            if (sourceFileLength == targetFileLength)
            {
                fileLengthsMatch = true;
                Console.WriteLine(String.Format("File length [{0} bytes] of both locations match.", sourceFileLength));
            }
            else
            {
                Console.WriteLine("File lengths of both locations do not match.");
            }

            if (sourceSHA1 == targetSHA1)
            {
                filesMatch = true;
                Console.WriteLine(String.Format("SHA1 hash [{0}] of both locations match.", sourceSHA1));
            }
            else
            {
                Console.WriteLine("SHA1 hashes of both locations do not match.");
            }
            Console.WriteLine(" ");

            return (filesMatch && fileLengthsMatch);
        }

        /// <summary>
        /// Copy file to destination. Verify file integrity, using values obtained from original file if possible.
        /// </summary>
        /// <param name="file"></param>
        /// <param name="destination"></param>
        public void StageResourceFile(Byte[] file, String destination, String originalFileHash = "", String originalFileSize = "", String dateModifiedString = "")
        {
            Console.WriteLine("Starting StageResourceFile(...)");

            try
            {
                String targetDir = Path.GetDirectoryName(destination);
                String sourceDir = Path.GetDirectoryName(Application.ExecutablePath);

                if (!Directory.Exists(targetDir))
                {
                    Directory.CreateDirectory(targetDir);
                }

                Console.WriteLine(String.Format("Copying files to {0}.", destination));
                File.WriteAllBytes(destination, file);
            }
            catch (Exception ex)
            {
                Console.WriteLine(String.Format(@"Error Staging Resource file {0}: {1}", ex.Message, destination));
            }

            String sha1Hash = String.Empty;

            if (String.IsNullOrEmpty(originalFileHash))
            {
                SHA1 sha1 = SHA1.Create();
                sha1Hash = BitConverter.ToString(sha1.ComputeHash(file)).Replace("-", String.Empty);
            }
            else
            {
                sha1Hash = originalFileHash;
            }

            UInt64 fileSize = 0;

            if (String.IsNullOrWhiteSpace(originalFileSize))
            {
                fileSize = (UInt64)file.Length;
            }
            else
            {
                fileSize = Convert.ToUInt64(originalFileSize);
            }

            DateTime dateModified = DateTime.Now;
            if (! String.IsNullOrWhiteSpace(dateModifiedString))
            {
                dateModified = Convert.ToDateTime(dateModifiedString);
            }

            Boolean filesMatch = HashCheck(sha1Hash, fileSize, destination);

            if (!filesMatch)
            {
                // 1392 = File is Corrupt
                Environment.Exit(1392);
            }

            File.SetLastWriteTime(destination, dateModified);
        }
    }
}
